package top.tenebrous.yzl.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.models.auth.In;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.roaringbitmap.RoaringArray;
import org.roaringbitmap.RoaringBitmap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.tenebrous.yzl.basic.constant.ResultCode;
import top.tenebrous.yzl.basic.model.CtsResult;
import top.tenebrous.yzl.entity.TestNo;
import top.tenebrous.yzl.mapper.TestNoMapper;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author yazhang6
 */
@RestController
@RequestMapping("test")
@Slf4j
public class TestNoController {

    @Autowired
    private TestNoMapper testNoMapper;

    @Resource
    private RedisTemplate<String, Long> redisTemplate;

    private final AtomicInteger bits = new AtomicInteger(5);

    @Resource
    private ApplicationContext applicationContext;

    private Random random = new Random();;

    private static List<String> areaCodes = new ArrayList<>();

    static {
        areaCodes.add("340171404001");
        areaCodes.add("340171401002");
        areaCodes.add("340322000000");
        areaCodes.add("340171404000");
        areaCodes.add("340322101000");
        areaCodes.add("340322100201");
        areaCodes.add("340322100209");
        areaCodes.add("340322100212");
        areaCodes.add("340322100206");
        areaCodes.add("340322100204");
        areaCodes.add("340322100202");
        areaCodes.add("340322100211");
        areaCodes.add("340322100200");
        areaCodes.add("340322100011");
        areaCodes.add("340322100010");
        areaCodes.add("340322100210");
        areaCodes.add("340322100009");
        areaCodes.add("340322100203");
        areaCodes.add("340322100208");
        areaCodes.add("340322100012");
        areaCodes.add("340322100207");
        areaCodes.add("340322100000");
        areaCodes.add("340322100205");
    }


    @GetMapping("/test/setBitMap/{areaNo}/{orgId}")
    public CtsResult testSetBitMap(@PathVariable("areaNo") String areaNo, @PathVariable("orgId") String orgId) {
        List<TestNo> nos = testNoMapper.selectList(new LambdaQueryWrapper<TestNo>()
                .like(TestNo::getTestArchriveNo, areaNo)
        );
        if (Objects.isNull(nos)) {
            return CtsResult.success();
        }
        ThreadPoolTaskExecutor localBootAsyncExecutor = (ThreadPoolTaskExecutor) applicationContext.getBean("localBootAsyncExecutor");

        int size = 3000; // 每个子列表的大小
        List<List<TestNo>> subLists = new ArrayList<>();
        for (int i = 0; i < nos.size(); i += size) {
            int endIndex = Math.min(i + size, nos.size());
            subLists.add(nos.subList(i, endIndex));
        }

        for (List<TestNo> subList : subLists) {
            localBootAsyncExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    for (TestNo no : subList) {
                        // 获取自增序列
                        String archriveNo = no.getTestArchriveNo();
                        String substring = archriveNo.substring(12);
                        log.info("截取前:{} 截取后:{} 转换后:{}", archriveNo, substring, Long.parseLong(substring));
                        redisTemplate.opsForValue().setBit(areaNo, Integer.parseInt(substring), true);
                    }
                }
            });
        }



        return CtsResult.success();
    }

    @SneakyThrows
    @GetMapping("/test/createData")
    public CtsResult createData() {
        ThreadPoolTaskExecutor localBootAsyncExecutor = (ThreadPoolTaskExecutor) applicationContext.getBean("localBootAsyncExecutor");
        CountDownLatch count = new CountDownLatch(areaCodes.size());
        for (String areaCode : areaCodes) {
            localBootAsyncExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 30000; i++) {
                        int randomInt = random.nextInt(11);
                        if (randomInt % 3 == 0) {       // 随机数余5==0
                            String numb = getNumb(i + randomInt + "");
                            TestNo testNo1 = new TestNo();
                            testNo1.setTestArchriveNo(areaCode + numb);
                            testNo1.setCreateDate(new Date());
                            testNo1.setUpdateDate(new Date());
                            testNoMapper.insert(testNo1);
                        }
                    }
                    count.countDown();
                }
            });
        }
        count.await();
        return CtsResult.success();

    }

    @GetMapping("/test/get/{areaNo}")
    public CtsResult testGetBitMap(@PathVariable("areaNo") String areaNo) {
        Long index = redisTemplate.execute((RedisCallback<Long>) cbk -> cbk.bitPos(areaNo.getBytes(), false));
        log.info("bitMap中最先生成为0的索引:{}", index);
        // 数据入库
        String numb = getNumb(index + "");
        String testNo = areaNo + numb;
        // 校验标号
        TestNo no = testNoMapper.selectOne(new LambdaQueryWrapper<TestNo>().eq(TestNo::getTestArchriveNo, testNo));
        if (Objects.isNull(no)) {
            TestNo testNo1 = new TestNo();
            testNo1.setTestArchriveNo(testNo);
            testNo1.setCreateDate(new Date());
            testNo1.setUpdateDate(new Date());
            testNoMapper.insert(testNo1);
            log.info("生成编号:{}", testNo);
            redisTemplate.opsForValue().setBit(areaNo, index, true);
        } else {
            log.info("bitMap索引:{} 对应的编号已存在，存在编号:{}", index, JSON.toJSONString(no));
        }
        return CtsResult.success(testNo);
    }

    @GetMapping("/getCode/{areaNo}/{orgId}")
    public CtsResult getCode(@PathVariable("areaNo") String areaNo, @PathVariable("orgId") String orgId) {
        List<Integer> codes = testNoMapper.selectCode(orgId);
        // 判断是否有 0
        boolean contains = codes.contains(0);
        if (contains) {
            // 过滤掉 0 ; 因为数据从1开始，有0说明已经存在数据从1开始的数据，主要是为了方便从头开始过滤
            codes = codes.stream().distinct().filter(x -> x != 0).collect(Collectors.toList());
        }
        // 从头开始获取最小的一位数据
        Integer code = codes.get(0);
        if (!contains && code >= 1) {
            code = 1;
        }

        String result = areaNo + String.format("%0" + bits + "d", code);
        if (StringUtils.isNotBlank(result)) {
            // 查询是否有相同数据
            TestNo testNo = testNoMapper.selectOne(new LambdaQueryWrapper<TestNo>().eq(TestNo::getTestArchriveNo, result));
            if (Objects.nonNull(testNo)) {
                return CtsResult.error(ResultCode.C500, "编号:[" + result + "]重复; 重复id:[" + testNo.getId() + "]");
            }
            TestNo testNo1 = new TestNo();
            testNo1.setTestArchriveNo(result);
            testNo1.setCreateDate(new Date());
            testNo1.setUpdateDate(new Date());
            testNoMapper.insert(testNo1);
            log.info("新增数据成功");
        }
        return CtsResult.success(result);
    }

    private static String getNumb(String substringCode) {
        String newNumb = "";
        Integer integer = Integer.valueOf(substringCode);
        DecimalFormat decimalFormat = new DecimalFormat("00000");
        if (integer < 99999){
            newNumb = decimalFormat.format(integer);
            return  newNumb;
        }else {
            newNumb = decimalFormat.format(integer);
            return newNumb;
        }
    }

    public static void main(String[] args) {
        try {
            System.out.println("开始执行业务数据啦");
            int i =1 / 0 ;
        } catch (Exception e) {
            System.out.println("回滚啦");
        } finally {
            System.out.println("哦吼，执行完了");
        }
    }
}
